package com.wolfking.converter.transform.extend;

import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Lists;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.wolfking.converter.config.DynamicDataSourceContext;
import com.wolfking.converter.exception.ParamVerifyException;
import com.wolfking.converter.service.JdbcTemplateService;
import com.wolfking.converter.transform.AbstractExtendTransform;
import com.wolfking.converter.util.ConverterSpringContext;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations;
import org.springframework.util.CollectionUtils;

import java.util.List;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

@Slf4j
public class SqlQueryTransform extends AbstractExtendTransform<Object> {

    private QueryConfig queryConfig;

    private final Gson gson = new GsonBuilder().setPrettyPrinting().disableHtmlEscaping().create();

    //缓存10秒
    private static final Cache<CacheKey, Object> QUERY_CACHE = CacheBuilder.newBuilder()
            .expireAfterWrite(10, TimeUnit.SECONDS).maximumSize(10000)
            .concurrencyLevel(100)
            .build();

    private static final Cache<CacheKey, Object> QUERY_CACHE_LONG = CacheBuilder.newBuilder()
            .expireAfterWrite(1, TimeUnit.MINUTES).maximumSize(10000)
            .concurrencyLevel(100)
            .build();

    public SqlQueryTransform() {
    }

    public SqlQueryTransform(String config) {
        queryConfig = gson.fromJson(config, QueryConfig.class);
    }

    @Override
    public Object transform(Object value) throws Exception {
        if (Objects.isNull(value)) {
            return null;
        }
        if (value instanceof String && StringUtils.isBlank(value.toString())) {
            return value;
        }
        CacheKey cacheKey = new CacheKey(queryConfig, value);
        Object oneCache = QUERY_CACHE_LONG.getIfPresent(cacheKey);
        if (Objects.nonNull(oneCache)) {
            log.info("{} hit long cache {}", cacheKey.getObject(), oneCache);
            return oneCache;
        }
        Object o = QUERY_CACHE.getIfPresent(cacheKey);
        if (Objects.nonNull(o)) {
            log.info("{} hit empty cache {}", cacheKey, o);
            return o;
        }

        DynamicDataSourceContext dynamicDataSourceContext = ConverterSpringContext.getBean(DynamicDataSourceContext.class);
        try {
            if (StringUtils.isNotBlank(queryConfig.getDatasource())) {
                dynamicDataSourceContext.setDataSourceType(queryConfig.getDatasource());
            } else {
                dynamicDataSourceContext.clearDataSourceType();
            }
            NamedParameterJdbcOperations jdbcTemplate = ConverterSpringContext.getBean(JdbcTemplateService.class).getNamedParameterJdbcTemplate();
            if (!queryConfig.isOne()) {
                List<?> arg = jdbcTemplate.queryForList(queryConfig.getSql(), ImmutableMap.of("arg", value), Class.forName(queryConfig.getClassname()));
                if (CollectionUtils.isEmpty(arg)) {
                    if (queryConfig.isReturnEmpty()) {
                        if (queryConfig.isCacheAble()) {
                            QUERY_CACHE.put(cacheKey, Lists.newArrayList());
                        }
                        return Lists.newArrayList();
                    } else {
                        throw new ParamVerifyException(String.format("[%s] 参数转换失败，\n%s\n%s\n%s", queryConfig.getParamName(), this.getClass().getName(),
                                gson.toJson(queryConfig), "查到空list"));
                    }
                }
                if (queryConfig.isCacheAble()) {
                    QUERY_CACHE_LONG.put(cacheKey, arg);
                }
                return arg;
            } else {
                Object one = jdbcTemplate.queryForObject(queryConfig.getSql(), ImmutableMap.of("arg", value), Class.forName(queryConfig.getClassname()));
                if (Objects.nonNull(one) && queryConfig.isCacheAble()) {
                    QUERY_CACHE_LONG.put(cacheKey, one);
                }
                return one;
            }
        } catch (EmptyResultDataAccessException emptyResultDataAccessException) {
            log.warn("paramName 参数转换失败，查询到的数据为空");
            if (queryConfig.isReturnEmpty() && queryConfig.isCacheAble()) {
                QUERY_CACHE.put(cacheKey, "");
                return "";
            } else {
                throw new ParamVerifyException(String.format("[%s]  参数转换失败,查到0，实际需要1，\n%s\n%s\n%s", queryConfig.getParamName(), this.getClass().getName(),
                        gson.toJson(queryConfig), ExceptionUtils.getStackTrace(emptyResultDataAccessException)));
            }
        } finally {
            dynamicDataSourceContext.clearDataSourceType();
        }
    }

    @Data
    @ToString
    @EqualsAndHashCode
    private static class QueryConfig {

        private String datasource;

        private String sql;

        private String classname = String.class.getName();

        private boolean one = true;

        private String paramName;

        private boolean returnEmpty = false;

        private boolean cacheAble = true;
    }

    @Data
    @ToString
    @EqualsAndHashCode
    @AllArgsConstructor
    private static class CacheKey {
        private QueryConfig queryConfig;
        private Object object;
    }


    @Override
    public String getExampleConfig() {
        return "{\n" +
                "\t\"datasource\": \"fund\",\n" +
                "\t\"sql\": \"select partner_loan_no from loan where loan_key = :arg \",\n" +
                "\t\"classname\": \"java.lang.String\",\n" +
                "\t\"paramName\": \"资金loanKey\",\n" +
                "\t\"one\": true,\n" +
                "\t\"returnEmpty\": false,\n" +
                "\t\"cacheAble\": true\n" +
                "}";
    }

    @Override
    public String getExampleConfigDesc() {
        return "{\n" +
                "\t\"datasource\": \"数据源的名称\",\n" +
                "\t\"sql\": \"查询的SQL，只有一个参数arg \",\n" +
                "\t\"classname\": \"jdbcTemplate查询返回的类型，默认String\",\n" +
                "\t\"paramName\": \"参数名\",\n" +
                "\t\"one\": 查询是否返回一个，默认true,\n" +
                "\t\"returnEmpty\": 是否返回空，默认false,\n" +
                "\t\"cacheAble\": 是否可以缓存默认true\n" +
                "}";
    }
}
